#include "PerlinNoise.hpp"
#include "terrain_file_utils.h"
#include <iostream>
#include <iomanip>
#include <sstream>
#include <omp.h>

int main()
{
	std::vector<std::size_t> scales{ 500, 1000, 5000, 10000, 20000 };
	std::vector<double> frequencies{ 4, 8, 16, 32, 64 };
	std::vector<double> thresholds{ 0.88, 0.92, 0.96 };

	const std::int32_t octaves = 8.0;
	const std::uint32_t seed = 12345;

	const std::string path{ "../data/" };

	{	// print parameters
		std::cout << std::string(40, '=') << '\n';
		std::cout << "Perlin Noise Parameters for Test Terrains\n";
		std::cout << std::string(40, '=') << '\n';

		std::cout << "Fixed Parameters:\n";
		std::cout << std::setw(10) << "Octaves:" << std::setw(8) << octaves << '\n';
		std::cout << std::setw(10) << "Seed:" << std::setw(8) << seed << '\n';
		std::cout << '\n';

		std::cout << "Test Parameters:\n";
		std::cout << std::setw(15) << "Scales:"; 
		for (const auto& scale : scales) { std::cout << std::setw(8) << scale; } 
		std::cout << '\n';

		std::cout << std::setw(15) << "Frequencies:";
		for (const auto& frequency : frequencies) { std::cout << std::setw(8) << frequency; }
		std::cout << '\n';

		std::cout << std::setw(15) << "Thresholds:";
		for (const auto& threshold : thresholds) { std::cout << std::setw(8) << threshold; }
		std::cout << '\n';
		std::cout << '\n';

		std::cout << "Save Path:\n";
		std::cout << "  " << path << '\n';
		std::cout << '\n';
	}

	std::size_t counter = 0;
	std::size_t total = scales.size() * frequencies.size() * thresholds.size();
	std::cout << "Progress:\n";

	for (const auto& scale : scales)
	{
		for (const auto& frequency : frequencies)
		{
			for (const auto& threshold : thresholds)
			{
				RealScan::TerrainBinary terrain(scale, scale);

				const siv::PerlinNoise perlin { seed };
				const double fx{ frequency / terrain.width() };
				const double fy{ frequency / terrain.height() };

				#pragma omp parallel for
				for (std::int32_t x = 0; x < terrain.height(); ++x)
				{
					for (std::int32_t y = 0; y < terrain.width(); ++y)
					{
						double noise = perlin.octave2D_01((x * fx), (y * fy), octaves);
						const std::int8_t value = (noise > threshold)? 1:0;
						terrain.set(x, y, value);
					}
				}

				double occupancy = terrain.occupancy();
				
				std::stringstream ss;
				ss << path << 'f' << frequency << 's' << scale << 'p' << std::fixed << std::setprecision(3) << occupancy << ".bin"; 
				terrain.save(ss.str());

				// print progress
				counter++;
				double progress = static_cast<double>(counter) / total;
				const int width = 30;

				std::cout << "\r  [";
				int pos = static_cast<int>(width * progress);
				for (int j = 0; j < width; j++)
				{
					if (j < pos)
						std::cout << '=';
					else if (j == pos)
						std::cout << '>';
					else
						std::cout << ' ';
				}
				std::cout << "] " << int(progress * 100.0) << '%';
				std::cout.flush();
			}
		}
	}
	std::cout << '\n';
	std::cout << "Done.\n";

    return 0;
}